/**
 * Created by sine on 2017/8/31.
 */


/**
 * 元素事件相关的数据版定到元素的 $enventData 中
 * $eventData: {
 *      mouseOver: Boolean  标识鼠标是否悬停于元素之上
 // *      state: number    按二进制位表示
 // *          1: enter
 // *          2: move
 // *          3: leave
 // *          4: down
 // *          5: up
 * }
 *
 */

var $e = {};

$(function(){

    var defaultListName = "_default";


    /////////////////////////////////////////
    //develop
    /////////////////////////////
    // Array.prototype = Object.create([]);
    // Array.prototype.push = function(v){
    //     if(this.onchange)
    //         this.onchange.call(this);
    //     this.__proto__.__proto__.push(v);
    // };
    //////////////////////////////

    if(!Object.keys){
        Object.keys = function(obj){
            if(typeof obj !== "object")
                throw "type error";
            var keys = [];
            for(var key in obj){
                keys.push(key);
            }
            return keys;
        }
    }

    if(!Array.prototype.forEach){
        Array.prototype.forEach = function(f){
            for(var i = 0; i < this.length; i++){
                f(this[i], i, this);
            }
        };
    }

    if(!Array.prototype.map){
        Array.prototype.map = function(f){
            var result = [];
            for(var i = 0; i < this.length; i++){
                result[i] = f(this[i], i, this);
            }
            return result;
        };
    }

    Array.prototype.removeObj = function(obj){
        var index = -1;
        for(var i = 0; i < this.length; i++){
            if(this[i] === obj){
                index = i;
                break;
            }
        }
        this.splice(index, 1);
        return index;
    };

    Array.prototype.clone = function(){
        var result = [];
        for(var i = 0; i < this.length; i++){
            result.push(this[i]);
        }
        return result;
    };

    Array.prototype.append = function(){
        for(var i = 0; i < arguments.length; i++){
            var arr = arguments[i];
            if(isNaN(arr.length)){
                return;
            }else{
                for(var j = 0; j < arr.length; j++){
                    this.push(arr[j]);
                }
            }
        }
    };

    /**
     * 刷新事件触发元素列表
     * @param els
     */
    $e.refresh = function(els, name){
        name = name || defaultListName;

        if(!senders[name]) senders[name] = [], stateList[name] = [];
        senders[name].length = 0;
        if(els)
        for(var i = 0; i < els.length; i++){
            objInit(els[i], name);
            senders[name].push(els[i]);
        }
    };
    $e.add = function(els, name){
        name = name || defaultListName;
        if(!senders[name]) senders[name] = [], stateList[name] = [];
        for(var i = 0; i < els.length; i++){
            objInit(els[i], name);
            senders[name].push(els[i]);
        }
    };
    $e.remove = function(els, name, delData){
        name = name || defaultListName;
        if(!senders[name]) return;
        for(var i = 0; i < els.length; i++){
            senders[name].removeObj(els[i]);
            if(delData)
                delete els[i].$eventData[name];
        }
    };
    $e.getStateLength = function(name){
        name = name || defaultListName;
        return stateList[name].length;
    };

    /**
     * 清除事件队列
     * @param name 队列名称
     * @param del 删除数据
     */
    $e.clear = function(name, del){

        name = name || defaultListName;
        if(del === true){
            senders[name].forEach(function(v){
                delete v.$eventData[name];
            });
            stateList[name].forEach(function(v){
                v.forEach(function(v){
                    delete v.el.$eventData[name];
                });
            });
        }
        senders[name].length = 0;
        stateList[name].length = 0;
    };

    /**
     * 禁用对应事件栈
     * @param name
     */
    $e.disable = function(name){
        name = name || defaultListName;
        disabledMark[name] = true;
    };
    /**
     * 启用对应事件栈
     * @param name
     */
    $e.enable = function(name){
        name = name || defaultListName;
        delete disabledMark[name];
    };

    /**
     * 推入一个监听状态
     * @param els
     * @param name 队列名称
     * @param unbind 移除事件绑定
     */
    $e.statePush = function(els, name, unbind){
        if(typeof name === "boolean"){
            unbind = name;
            name = defaultListName;
        }
        name = name || defaultListName;

        if(!senders[name]){
            senders[name] = [];
            stateList[name] = [];
        }
        if(senders[name].length)
        stateList[name].push(senders[name].map(function(v){
            return {
                el: v,
                state: {
                    mouseenter: v.$eventData[name].$_mouseenter.clone(),
                    mousemove: v.$eventData[name].$_mousemove.clone(),
                    mouseleave: v.$eventData[name].$_mouseleave.clone(),
                    mousedown: v.$eventData[name].$_mousedown.clone(),
                    mouseup: v.$eventData[name].$_mouseup.clone()
                }
            };
        }));

        if(unbind)
            $(senders[name]).$offAll(name);
        $e.refresh(els, name);

        // if(name === "ctl"){
        //     console.log(stateList["ctl"], stateList["ctl"].length, senders["ctl"]);
        // }
    };

    // setInterval(function(){
    //     console.log(stateList["ctl"][0]);
    // }, 1000);

    /**
     * 监听状态回退
     * @param name 队列名称
     * @param step 回退步数
     * @return Array 返回当前状态（含）到目标状态（不含）之间的状态的数组
     */
    $e.stateBack = function(step, name){
        if(typeof step === "string"){
            name = step;
            step = 1;
        }else{
            name = name || defaultListName;
            if(step == null)
                step = 1;
            step = Number(step);
            if(!step)
                return null;
        }

        var result = [];

        if(step > stateList[name].length)
            step = stateList[name].length;

        if(step === 0){
            senders[name].length = 0;
            return [];
        }

        // if(name === "ctl"){
        //     console.log("pre");
        //     console.log(senders);
        // }
        while(step--){
            result.push(stateList[name].pop());
        }
        // if(name === "ctl"){
        //     console.log("after");
        //     console.log(senders);
        // }

        // if(name==="ctl")
        //     alert("kaka");
        //
        // console.log(result[0]);
        //
        // try{
        //     console.log("e-", document.body.$eventData["ctl"].$_mouseup.length);
        // }catch(e){
        //     console.log("e---undefined");
        // }

        $(senders[name]).$offAll(name);
        senders[name] = result[result.length - 1].map(function(v){
            var data =  v.el.$eventData[name];
            data.$_mouseenter = v.state.mouseenter;
            data.$_mousemove = v.state.mousemove;
            data.$_mouseleave = v.state.mouseleave;
            data.$_mousedown = v.state.mousedown;
            data.$_mouseup = v.state.mouseup;
            // console.log("e-", v.el.$eventData["ctl"]);
            // console.log("e-", v.el.nodeName);
            return v.el;
        });

        // try{
        //     console.log("e-", document.body.$eventData["ctl"].$_mouseup.length);
        // }catch(e){
        //     console.log("e---undefined");
        // }

        return result;
    };


    $.fn.$on = function(eventName, handler, queueName){
        queueName = queueName || defaultListName;

        this.each(function(){
            this.$eventData[queueName].addEvent(eventName, handler);
        });
        return this;
    };
    $.fn.$off = function(eventName, handler, queueName){
        if(typeof handler === "string"){
            queueName = handler;
            handler = null;
        }
        queueName = queueName || defaultListName;

        this.each(function(){
            this.$eventData[queueName].removeEvent(eventName, handler);
        });
        return this;
    };
    $.fn.$offAll = function(queueName){
        queueName = queueName || defaultListName;
        this.each(function(){
            this.$eventData[queueName].removeEvent();
        });
    };
    $.fn.$refresh = function(name){$e.refresh(this, name); return this;};
    $.fn.$add = function(name){$e.add(this, name); return this;};
    $.fn.$remove = function(name, delData){$e.remove(this, name, delData); return this;};

    $.fn.$init = function(name, force){
        this.each(function(){
            objInit(this,name, force);
        });
        return this;
    };
    $.fn.$push = function(name, unbind){$e.statePush(this, name, unbind); return this;};

    $.fn.$saveState = function(stateName, state, queueName){
        queueName = queueName || defaultListName;
        if(typeof state === "string"){
            queueName = state;
            state = null;
        }
        this.each(function(){
            this.$eventData[queueName].saveState(stateName, state);
        });
        return this;
    };
    $.fn.$changeState = function(stateName, queueName){
        queueName = queueName || defaultListName;
        this.each(function(){
            this.$eventData[queueName].changeState(stateName);
        });
        return this;
    };
    $.fn.$del = function(name){
        name = name || defaultListName;
        this.each(function(){
            delete this.$eventData[name];
        });
    };

    /**
     * 初始化一个对象
     */
    function objInit(obj, name, force){
        obj = $(obj)[0];

        name = name || defaultListName;
        if(!obj.$eventData) obj.$eventData = {};

        if(!obj.$eventData[name] || force)
        obj.$eventData[name] = {
            //事件函数队列
            $_mouseenter: [],
            $_mousemove: [],
            $_mouseleave: [],
            $_mousedown: [],
            $_mouseup: [],
            ///////////////////
            $_listeners: {},
            ///////////////
            /**
             * @param filter: function(e)
             */
            addEvent: function(name, func, filter){
                name = "$_" + name;
                func.$filter = filter;
                if(this[name])
                    this[name].push(func);
            },
            removeEvent: function(name, func){
                if(name){
                    name = "$_" + name;
                    if(this[name]){
                        if(func)
                            this[name].removeObj(func);
                        else
                            this[name].length = 0;
                    }
                }else{
                    this.$_mouseenter.length = 0;
                    this.$_mousemove.length = 0;
                    this.$_mouseleave.length = 0;
                    this.$_mousedown.length = 0;
                    this.$_mouseup.length = 0;
                }
            },
            /**
             * 切换监听状态
             * @param stateName
             */
            changeState: function(stateName){
                var state = this.$_listeners[stateName];
                if(state){
                    this.$_mouseenter = state.$_mouseenter.clone();
                    this.$_mousemove = state.$_mousemove.clone();
                    this.$_mouseleave = state.$_mouseleave.clone();
                    this.$_mousedown = state.$_mousedown.clone();
                    this.$_mouseup = state.$_mouseup.clone();
                }
            },
            /**
             * 保存监听状态
             */
            saveState: function(stateName, state){
                if(stateName){
                    if(state){
                        this.$_listeners[stateName] = {
                            $_mouseenter: state.mouseenter ? (typeof state.mouseenter === "function" ? [state.mouseenter] : (state.mouseenter.length ? state.mouseenter.clone() : [])) : [],
                            $_mousemove: state.mousemove ? (typeof state.mousemove === "function" ? [state.mousemove] : (state.mousemove.length ? state.mousemove.clone() : [])) : [],
                            $_mouseleave: state.mouseleave ? (typeof state.mouseleave === "function" ? [state.mouseleave] : (state.mouseleave.length ? state.mouseleave.clone() : [])) : [],
                            $_mousedown: state.mousedown ? (typeof state.mousedown === "function" ? [state.mousedown] : (state.mousedown.length ? state.mousedown.clone() : [])) : [],
                            $_mouseup: state.mouseup ? (typeof state.mouseup === "function" ? [state.mouseup] : (state.mouseup.length ? state.mouseup.clone() : [])) : []
                        }
                    }else{
                        //没有提供状态对象则保存当前
                        this.$_listeners[stateName] = {
                            $_mouseenter: this.$_mouseenter.clone(),
                            $_mousemove: this.$_mousemove.clone(),
                            $_mouseleave: this.$_mouseleave.clone(),
                            $_mousedown: this.$_mousedown.clone(),
                            $_mouseup: this.$_mouseup.clone()
                        };
                    }
                }
            }
        };
    }


    var senders = {};
    senders[defaultListName] = [];

    var stateList = {};
    stateList[defaultListName] = [];

    var disabledMark = {};


    // function activator_mouseenter(e, name){
    //     var el = this;
    //     this.$eventData[name].$_mouseenter.forEach(function(v){
    //         v.call(el, e);
    //     });
    // }
    // function activator_mousemove(e, name){
    //     var el = this;
    //     this.$eventData[name].$_mousemove.forEach(function(v){
    //         v.call(el, e);
    //     });
    // }
    // function activator_mouseleave(e, name){
    //     var el = this;
    //     this.$eventData[name].$_mouseleave.forEach(function(v){
    //         v.call(el, e);
    //     });
    // }
    //
    // function activator_mousedown(e, name){
    //     var el = this;
    //     this.$eventData[name].$_mousedown.forEach(function(v){
    //         v.call(el, e);
    //     });
    // }
    // function activator_mouseup(e, name){
    //     var el = this;
    //     this.$eventData[name].$_mouseup.forEach(function(v){
    //         v.call(el, e);
    //     });
    // }

    /**\
     * @param queue
     *  {el: HtmlElement, ev: [Function]}
     */
    function active(queue, e){
        queue.forEach(function(v){
            v.ev.forEach(function(f){
                f.call(v.el, e);
            });
        });
    }


    var flag = true;
    setInterval(function(){
        flag = true;
    }, 1000);

    $(document.body).on("mousemove", function(e){

        if(flag){
            flag = false;
            // console.log(senders);
            // console.log(senders[defaultListName]);
            // console.log(stateList["ctl"]);
        }

        var queue = [];

        Object.keys(senders).forEach(function(name){
            if(disabledMark[name]) return;
            senders[name].forEach(function(el){

                // if($(senders).filter("*[class|=col]").length){
                // }

                // console.log(senders.length);

                var qe = {el: el, ev: []};  //queue element

                var data = el.$eventData[name];
                var mouseOver = check_mouseOver.call(el, e);
                if(mouseOver && !data.mouseOver && data.$_mouseenter.length > 0){
                    // activator_mouseenter.call(el, e, name);
                    qe.ev.append(data.$_mouseenter);
                }else if(!mouseOver && data.mouseOver && data.$_mouseleave.length > 0){
                    // activator_mouseleave.call(el, e, name);
                    qe.ev.append(data.$_mouseleave);
                }
                data.mouseOver = mouseOver;

                if(data.mouseOver && data.$_mousemove.length > 0){
                    // activator_mousemove.call(el, e, name);
                    qe.ev.append(data.$_mousemove);
                }
                queue.push(qe);
            });
        });
        active(queue, e);

    }).on("mousedown", function(e){
        var queue = [];
        // console.log(senders["ctl"]);
        Object.keys(senders).forEach(function(name){
            senders[name].clone().forEach(function(el){
                // if(!el.$eventData)
                //     objInit(el);

                var qe = {el: el, ev: []};

                var data = el.$eventData[name];
                var mouseOver = check_mouseOver.call(el, e);
                data.mouseOver = mouseOver;
                if(mouseOver && data.$_mousedown.length > 0){
                    // activator_mousedown.call(el, e, name);
                    qe.ev.append(data.$_mousedown);
                }
                queue.push(qe);
            });
        });
        active(queue, e);

    }).on("mouseup", function(e){
        // try{
        //     senders["ctl"].forEach(function(v){
        //         console.log("-------------------------------", v.$eventData["ctl"].$_mouseup);
        //     });
        // }catch(e){ }
        var queue = [];
        Object.keys(senders).forEach(function(name){
            senders[name].clone().forEach(function(el){
                // if(!el.$eventData)
                //     objInit(el);
                var qe = {el: el, ev: []};
                var data = el.$eventData[name];
                var mouseOver = check_mouseOver.call(el, e);
                data.mouseOver = mouseOver;
                if(mouseOver && data.$_mouseup.length > 0){
                    // activator_mouseup.call(el, e, name);
                    qe.ev.append(data.$_mouseup);
                }
                queue.push(qe);
            });
        });
        active(queue, e);
        // console.log(senders["ctl"]);
    });

//////////////////////////////////////
//functions
////////////

    function get_cornerPoint(el){
        var ltpp = get_position(el);
        var rbpp = [ltpp[0] + el.offsetWidth, ltpp[1] + el.offsetHeight];
        return {
            lt: ltpp,
            rb: rbpp
        };
    }
    function check_mouseOver(e){
        var ltpp = get_position(this);   // left top point position
        var rbpp = [ltpp[0] + this.offsetWidth, ltpp[1] + this.offsetHeight];

        return e.pageX >= ltpp[0] &&
            e.pageX <= rbpp[0] &&
            e.pageY >= ltpp[1] &&
            e.pageY <= rbpp[1];
    }
    function get_position(el){
        el = $(el)[0];
        var result = [el.offsetLeft, el.offsetTop];
        var root = document.documentElement;
        if($(el).css("position") === "fixed"){
            result[0] += window.scrollX || root.scrollLeft;
            result[1] += window.scrollY || root.scrollTop;
        }else
            try{
                $(el).parents().not("html,body").each(function(){
                    var position = $(this).css("position");
                    if(position === "fixed"){
                        result[0] += this.offsetLeft + (window.scrollX || root.scrollLeft);
                        result[1] += this.offsetTop + (window.scrollY || root.scrollTop);
                        throw 0;
                    }else if(position !== "static"){
                        result[0] += this.offsetLeft;
                        result[1] += this.offsetTop;
                    }
                });
            }catch(e){ }
        return result;
    }
});
